题目
题目描述
A国有n座城市,编号从1到n,城市之间有m条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。
输入输出格式
输入格式:
第一行有两个用一个空格隔开的整数n,m表示A国有n座城市和m条道路。
接下来m行每行3个整数 x, y, z每两个整数之间用一个空格隔开,表示从x号城市到y号城市有一条限重为z的道路。注意:x不等于y,两座城市之间可能有多条道路 。
接下来一行有一个整数 q,表示有 q 辆货车需要运货。
接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意: x 不等于 y 。
输出格式:
共有q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出−1。
输入输出样例
输入样例1:
1  | 4 3  | 
输出样例1:
1  | 3  | 
说明
对于 30%的数据,0 < n < 1,000,0 < m < 10,000,0 < q< 1,000;
对于 60%的数据,0 < n < 1,000,0 < m < 50,000,0 < q< 1,000;
对于 100%的数据,0 < n < 10,000,0 < m < 50,000,0 < q< 30,000,0 ≤ z ≤ 100,000
题解
部分内容摘自2013年鉴
其实这道题就是比较简单的图论题(作为最后一天的最后一题来讲)
首先对图做最大生成树(方法和最小生成树一样),得到的是一个森林。对于每一辆车,设其起点、终点分别为x,y,如果x,y连通,那么x,y在一棵树上,该火车最多能运送的货物为x,y之间的路径中的最小的边c。因为如果在原图中存在一条路径连接x,y,且所有的边的边权都大于c,那么最大生成树一定求错了(因为将该路径上的边加到生成树中去,必然形成包括c边在内的一个环,此时删除c边可以得到更大的生成树)
所以此题变成了求各个点对在树种路径上的最小边。  
方法一:暴力
就是去实现上面的步骤,求LCA的时候暴力来求,不用倍增,可以AC(具体见暴力),如果是一条链的话会被卡,然而并没有这种数据。
方法二:倍增
此时我们可以用倍增的子项求某两个点的路径上的最小边。首先纪录每个点向上(向树根)走$v(v=2^0,2^1,\dots)$步所经过的路径中最小边是多少。对于一个询问x,y,可以求出其最近公共祖先z,然后求x,z之间的最小边和y,z之间的最小边。
时间复杂度为$O(mlogm+n+logn)$
方法三:
标称用的方法并不是倍增法,只需要在最大生成树的里面加一个求解的询问就行。首先用一个数组ans表示最终的答案,ans[i]表示第i个询问最多能运的货物数量。再用kruskal算法中,每将一条边加到最大生成树里面,那么这条边会将连个点集合并到一起。
如果某个询问i,他的两个点x,y分别属于这两个点集,那么这个询问的答案就是新加入的这条边的边权(因为此时x,y已经在一棵树中,所以x,y之间有路径。由于Kruskal求最大生成树时是由边权从大到小加入的,所以当前边是路径上边权最小的边)我们只需要在每个电商挂一个询问的链,每次询问时将两个询问的链合并到一起,合并的时候选取询问较少的那个链进行扫描并更新答案。所以对链的操作的时间复杂度为[合并次数+每次合并时扫描的询问个数和]。时间的小号主要在扫描,但是我们可以发现,每次扫描都只扫描了较少的那一部分,设长度为L,并且合并后询问的长度增长到2L以上。所以总的时间复杂度为$O(mlogm+qlogq)$
代码
LCA暴力,代码来自本校神犇yanyu1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
using namespace std;
const int maxn=11000;
const int maxm=51000;
int head[maxn],Next[maxm<<1],ver[maxm<<1],weigh[maxm<<1];
int f[maxn],fa[maxn],dis[maxn],deep[maxn];
int n,m,q,tot;
struct node{
    int x,y,weigh;
}edge[maxm<<1];
 
inline void add(int x,int y,int w){
    ver[++tot]=y;weigh[tot]=w;Next[tot]=head[x];head[x]=tot;
}
 
bool cmp(node a,node b){
    return a.weigh > b.weigh;    
}
 
int find(int x){
    return fa[x]==x?x:fa[x]=find(fa[x]);
}
 
void dfs(int x){
    for(int i=head[x];i;i=Next[i]){
        int y=ver[i];
        if(deep[y]==0){
            dis[y]=weigh[i];
            deep[y]=deep[x]+1;
            f[y]=x;
            dfs(y);
        }
    }
}
 
int lca(int x,int y){
    int ans=1<<30;
    if(deep[x]>deep[y]) swap(x,y);
    while(deep[x]!=deep[y]){
        ans=min(ans,dis[y]);
        y=f[y];
    }
    while(x!=y){
        ans=min(ans,min(dis[x],dis[y]));
        x=f[x],y=f[y];
    }
    return ans;
}
 
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=m;i++)
        scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].weigh);
    sort(edge+1,edge+m+1,cmp);
    for(int i=1;i<=m;i++){
        int fx=find(edge[i].x);
        int fy=find(edge[i].y);
        if(fx!=fy){
            fa[fx]=fy;
            add(edge[i].x,edge[i].y,edge[i].weigh);
            add(edge[i].y,edge[i].x,edge[i].weigh);
        }
    }
    for(int i=1;i<=n;i++)
        if(deep[i]==0) dfs(i);
    scanf("%d",&q);
    for(int i=1,x,y;i<=q;i++){
        scanf("%d%d",&x,&y);
        if(find(x)!=find(y)) printf("-1\n");
        else printf("%d\n",lca(x,y));
    }
    return 0;
}
倍增1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
using namespace std;
const int MAXN =50005;
int dep[MAXN];
int n,m;
struct node{
    int x,y,w;
}edge[MAXN];
int fa[MAXN];
int Nt[MAXN],Head[MAXN],wight[MAXN],to[MAXN],tot;
int f[20005][21],w[20005][21];
void add(int x,int y,int v){
    Nt[++tot]=Head[x];
    to[tot]=y;
    wight[tot]=v;
    Head[x]=tot;
}
int Find(int x){
    if(fa[x]!=x)fa[x]=Find(fa[x]);
    return fa[x]; 
}
bool cmp(node a,node b){
    return a.w>b.w;
}
void dfs(int x){
    for(int i=Head[x];i;i=Nt[i]){
        int y=to[i];
        if(dep[y])continue;
        dep[y]=dep[x]+1;
        f[y][0]=x;
        w[y][0]=wight[i];
        dfs(y);
    }
}
void kruskal(){
    sort(edge+1,edge+m+1,cmp);
    for(int i=1;i<=n;i++)fa[i]=i;
    for(int i=1;i<=m;i++){
        int fa_x=Find(edge[i].x),fa_y=Find(edge[i].y);
        if(fa_x==fa_y)continue;
        fa[fa_x]=fa_y;
        add(edge[i].x,edge[i].y,edge[i].w);
        add(edge[i].y,edge[i].x,edge[i].w);
    }
}
int lca(int x,int y){
    if(Find(x)!=Find(y)) return -1;
    int ans=1<<30;
    if(dep[x]>dep[y])swap(x,y);
    for(int i=20;i>=0;i--){
        if(dep[f[y][i]]>=dep[x]){
            ans=min(ans,w[y][i]);
            y=f[y][i];
        }
    }
    if(x==y)return ans;
    for(int i=20;i>=0;i--){
        if(f[x][i]!=f[y][i]){
            ans=min(ans,min(w[x][i],w[y][i]));
            y=f[y][i];
            x=f[x][i];
        }
    }
    ans=min(ans, min(w[x][0], w[y][0]));
    return ans;
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        scanf("%d%d%d",&edge[i].x,&edge[i].y,&edge[i].w);
    }
    kruskal();
    int q;
    cin>>q;
    for(int i=1;i<=n;i++){
        if(!dep[i]){
            dep[i]=1;
            dfs(i);
            f[i][0]=i;
            w[i][0]=1<<30;
        }
    }
    for(int i=1;i<=20;i++){
        for(int j=1;j<=n;j++){
            f[j][i]=f[f[j][i-1]][i-1];
            w[j][i]=min(w[j][i-1],w[f[j][i-1]][i-1]);
        }
    }
    for(int i=1;i<=q;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        printf("%d\n",lca(x,y));
    }
    return 0;
}